/*
 * Copyright Kulikov Victor 2009
 * Distributed under LGPL license

 * Modified by Pavel Shchelkun 2012
 * in his part of a diploma project
 * named DVGEditor <http://code.google.com/p/dataviewer/>
 * and  distributed under GNU GPL license.
 */
#include "editorscene.h"
#include "polygontool.h"
#include <QGraphicsSceneMouseEvent>
#include <QLabel>
#include "types.h"

//Length calculation
#define DIST2(x,y) x*x+y*y
//Threshold of length to clicked
#define DELTA 25
//Min ZValue for Poly
#define MIN_ZVALUE 5

EditorScene::EditorScene(QObject *parent) :
    QGraphicsScene(parent)
{
    m_lastObjectUID = 1;
    m_pObjectLabel = NULL;
    m_pContextMenu = NULL;
    m_pToolContextMenu = NULL;
    default_layer=-1;
    m_primitive=NULL;
    line=NULL;
    m_documentRect = this->sceneRect();
    m_itemColor = QBrush(QColor(0,162,232,255));
    m_linePen = QPen(Qt::darkGray, 3);
    m_linePen.setCosmetic(true);

    connect(this,SIGNAL(selectionChanged()),this,SLOT(innerSelectionChanged()));
}

EditorScene::EditorScene(qreal x, qreal y, qreal width, qreal height, QObject *parent) :
    QGraphicsScene(x,y,width,height,parent)
{
    m_lastObjectUID = 1;
    m_pObjectLabel = NULL;
    m_pContextMenu = NULL;
    m_pToolContextMenu = NULL;
    default_layer=-1;
    m_primitive=NULL;
    line=NULL;
    setMode(MoveItem);
    m_documentRect = this->sceneRect();
    m_itemColor = QBrush(QColor(0,162,232,255));
    m_linePen = QPen(Qt::darkGray, 3);
    m_linePen.setCosmetic(true);

    connect(this,SIGNAL(selectionChanged()),this,SLOT(innerSelectionChanged()));
}

EditorScene::~EditorScene()
{
    if(m_primitive)
     {
         delete m_primitive;
         m_primitive = NULL;
     }
    cleanLines();
}
//Getters setters
void EditorScene::setItemPen(const QPen &pen)
{
    this->m_lineColor = pen;
    foreach(QGraphicsItem*item,selectedItems())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           polygon->setPen(pen);
       }
    }
}
void EditorScene::setTextColor(const QColor &color)
{
    this->m_textColor = color;
}
void EditorScene::setLayerId(const int layer)
{
    this->default_layer = layer;
    foreach(QGraphicsItem*item,selectedItems())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           polygon->setLayerId(layer);
       }
    }
}
void EditorScene::setItemBrush(const QBrush &brush)
{
    this->m_itemColor = brush;
    foreach(QGraphicsItem*item,selectedItems())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           polygon->setBrush(brush);
       }
    }
}
QImage* EditorScene::renderScene(int layer)
{
     this->hideAll();
     QList<QGraphicsItem*> selected = this->selectedItems();
     foreach(QGraphicsItem* item,selected)
     {
         item->setSelected(false);
     }

     QImage* image=new QImage(this->width(), this->height(),QImage::Format_RGB888);
     QPainter painter(image);

    foreach(QGraphicsItem*item,this->items())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           polygon->setBinaryMode(true,layer);
       }
    }
     this->render(&painter);

     foreach(QGraphicsItem* item,selected)
     {
         item->setSelected(true);
     }
      foreach(QGraphicsItem*item,this->items())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);

       if(polygon)
       {
           polygon->setBinaryMode(false,layer);
       }
    }
     return image;
}
void EditorScene::setFont(const QFont &font)
{
    this->m_font = font;
}
QList<QGraphicsItem*> EditorScene::getFiltredSelectedItems()
{
    return this->excludeTools(this->selectedItems());
}

QList<QGraphicsItem *> EditorScene::getAllItems()
{
    return this->excludeTools(this->items());
}

QList<QGraphicsItem *> EditorScene::getAllPolygons()
{
    return this->includeTools(this->items());
}

bool EditorScene::isScenePoly(QGraphicsItem* item)
{
    if(scenepolygons.constEnd()!=scenepolygons.find(item))
    {
        return true;
    }
    return false;
}

qreal EditorScene::getMaxZValueInScenePoly()
{
    qreal zValue = MIN_ZVALUE;
    foreach(QGraphicsItem* item, scenepolygons.toList())
    {
        if (zValue <= item->zValue())
        {
            zValue = item->zValue();
        }
    }
    return zValue;
}

//New functionality
QGraphicsItem* EditorScene::addResizingItem(QGraphicsItem* item)
{
    if(-1==this->items().indexOf(item))
    {
        addItem(item);
    }
    MagicPolygon* mg = new MagicPolygon(++m_lastObjectUID,item);
    mg->setContextMenu(m_pContextMenu);
    mg->setToolContextMenu(m_pToolContextMenu);
    magicpolywerif.insert(mg);
    mg->hide();
    mg->setPen(this->m_lineColor);
    mg->setLayerId(this->default_layer);
    mg->setBrush(this->m_itemColor);
    addItem(mg);
    magicpoly.insert(item,mg);
    return mg;
}
 void EditorScene::setPrimitive(QGraphicsItem* primitive)
 {
     if(m_primitive)
     {
         delete m_primitive;
         m_primitive = NULL;
     }
     m_primitive = primitive;
 }
 void EditorScene::removeItem(QGraphicsItem* item)
 {
     MagicPolygon* mg = magicpoly.value(item);
     if(mg)
     {
        magicpolywerif.remove(mg);
        magicpoly.remove(item);
        scenepolygons.remove(item);
        QGraphicsScene::removeItem(mg);
        return;
     }
     QGraphicsScene::removeItem(item);
 }

void EditorScene::innerSelectionChanged()
{
    QList<QGraphicsItem*> items = this->selectedItems();
    QList<QGraphicsItem*> filtred = excludeTools(items);
    int fcount = filtred.count();
    if(!items.count())
    {
       hideAll();
       return;
    }
    if(fcount == 1)
    {
        hideOthers(filtred.first());
        showFromObj(filtred.first());
    }
    if(fcount > 1)
    {
        PolygonTool::hideAll();
        showObjectName(false);
    }
}
void EditorScene::cleanLines()
{
    foreach(QGraphicsLineItem* line,lines)
    {
        removeItem(line);
        delete line;
        line = NULL;
    }
    lines.clear();
    if(line)
    {
        removeItem(line);
        delete line;
        line = NULL;
    }
}
void EditorScene::setMode(Mode mode)
{
    cleanLines();
    this->m_mode = mode;
}


void EditorScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
     if (mouseEvent->button() == Qt::RightButton)
     {
         cleanLines();
         setMode(MoveItem);
         emit modeChanged();
         emit modeChanged(Tool::PointerItem);
         return  QGraphicsScene::mousePressEvent(mouseEvent);
     }

     if (mouseEvent->button() == Qt::MiddleButton)
     {
         setMode(HandItem);
         emit modeChanged();
         emit modeChanged(Tool::HandItem);
     }

     if (m_mode == HandItem)
     {
         //QMessageBox::information(0,"Cursor","Hand");
         return;
     }

//     if (mouseEvent->scenePos().x() < m_documentRect.left() ||
//         mouseEvent->scenePos().x() > m_documentRect.right() ||
//         mouseEvent->scenePos().y() < m_documentRect.top() ||
//         mouseEvent->scenePos().y() > m_documentRect.bottom())
//     {
//         return;
//     }

     switch (this->m_mode) {
         case InsertItem:
             if(m_primitive)
             {

                 QGraphicsItem* elem = addResizingItem(m_primitive);
                 elem->setPos(mouseEvent->scenePos());


                 m_primitive->setSelected(true);
                 m_primitive = NULL;
                 setMode(MoveItem);
                 emit modeChanged();
                 emit modeChanged(Tool::PointerItem);
             }
                break;
         case InsertPolygon:
             if (line != 0) {
                 lines.push_back(line);
                 if(lines.count() > 2)
                 {
                     QPointF closetest = lines.first()->line().p1() - mouseEvent->scenePos();

                     if(DIST2(closetest.x(),closetest.y())<DELTA)
                     {
                         QPolygonF polygon;
                         foreach(QGraphicsLineItem* line,lines)
                         {
                             polygon<<line->line().p2();
                         }
                         MagicPolygon* mg = new MagicPolygon(++m_lastObjectUID,NULL);
                         mg->setContextMenu(m_pContextMenu);
                         mg->setToolContextMenu(m_pToolContextMenu);
                         mg->setPolygon(polygon);
                         mg->setZValue(getMaxZValueInScenePoly() + 0.1);
                         addItem(mg);
                         scenepolygons.insert(mg);
                         mg->setPen(this->m_lineColor);
                         mg->setBrush(this->m_itemColor);
                         mg->setLayerId(this->default_layer);

                         line = 0;
                         cleanLines();
                         setMode(MoveItem);
                         emit modeChanged();
                         emit modeChanged(Tool::PointerItem);
                         mg->hideTools();
                         return;
                     }
                 }

             }
             line = new QGraphicsLineItem(QLineF(mouseEvent->scenePos(),
                                         mouseEvent->scenePos()));
             line->setZValue(1000);
             line->setPen(m_linePen);
             addItem(line);
             break;

     default:
         ;
     }
    QGraphicsScene::mousePressEvent(mouseEvent);
}
void EditorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (m_mode == InsertPolygon && line != 0)
    {
         QLineF newLine(line->line().p1(), mouseEvent->scenePos());
         line->setLine(newLine);
    }
    else if (m_mode == MoveItem)
    {
         QGraphicsScene::mouseMoveEvent(mouseEvent);
    }
}

void EditorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (m_mode == HandItem)
        return;
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
void EditorScene::hideAll()
{
    PolygonTool::hideAll();
    foreach(MagicPolygon* pol,magicpoly)
    {
        pol->hide();
    }
    showObjectName(false);
}

void EditorScene::hideTools()
{
    foreach(MagicPolygon* pol,magicpoly)
    {
        pol->hideTools();
    }
}

void EditorScene::save(QDataStream* out)
{
    int count=0;
    foreach(QGraphicsItem*item,this->items())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           count++;
       }
    }
    out->operator <<(count);
    foreach(QGraphicsItem*item,this->items())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           polygon->writeToStream(out);
       }
    }
}
void EditorScene::load(QDataStream* in)
{
    int count=0;
    in->operator >>(count);
    for(int i=0;i<count;i++)
    {
        int itemid=-1;
        in->operator >>(itemid);
        QGraphicsItem* item = Tool::createItem(Tool::ItemType(itemid));
        MagicPolygon* pol = NULL;
        if(item)
        {
            pol = dynamic_cast<MagicPolygon*>(addResizingItem( item));
        }
        else
        {
            pol = new MagicPolygon(++m_lastObjectUID,NULL);
            pol->setContextMenu(m_pContextMenu);
            pol->setToolContextMenu(m_pToolContextMenu);
            this->addItem(pol);
        }
        if(pol)
        {
            pol->readFromStream(in);
        }
    }
}
//================================================

void EditorScene::saveObjects(QTextStream *out)
{
    QString dvg;
    dvg = QString("<dvg version=\"1.0\" width=\"%1px\" height=\"%2px\">\n")
            .arg(documentRect().width())
            .arg(documentRect().height());
    out->operator <<(dvg);

    QString rect;
    rect = QString("\t<rect name=\"%1\" "
                   "x=\"%2\" y=\"%3\" width=\"%4\" height=\"%5\" "
                   "color=\"%6\" "
                   "polygons=\"%7\">\n")
            .arg(documentName())
            .arg(documentRect().left())
            .arg(documentRect().top())
            .arg(documentRect().width())
            .arg(documentRect().height())
            .arg(getColorStringRGBA(documentColor()))
            .arg(getAllPolygons().count());
    out->operator <<(rect);

    foreach(QGraphicsItem*item,getAllPolygons())
    {
       MagicPolygon* polygon = dynamic_cast<MagicPolygon*>(item);
       if(polygon)
       {
           polygon->saveObject(out);
       }
    }

    rect = "\t</rect>\n";
    out->operator <<(rect);

    dvg = "</dvg>\n";
    out->operator <<(dvg);
}

void EditorScene::loadObjects(QDomNodeList *list)
{
    for (int i = 0; i < list->count(); i++)
    {
        QString value = list->at(i).attributes().namedItem("id").nodeValue();
        int uid = value.mid(7).toInt();
        if (uid > m_lastObjectUID)
        {
            m_lastObjectUID = uid;
        }

        MagicPolygon* mg = new MagicPolygon(uid,NULL);
        mg->setContextMenu(m_pContextMenu);
        mg->setToolContextMenu(m_pToolContextMenu);
        mg->setPen(this->m_lineColor);
        mg->setLayerId(this->default_layer);
        if (mg->loadObject(list->at(i)))
        {
            addItem(mg);
            scenepolygons.insert(mg);
        }
        else
        {
            delete mg;
        }
    }
    hideAll();
}

//================================================

void EditorScene::showFromObj(QGraphicsItem* obj)
{
    MagicPolygon* poly = magicpoly.value(obj);
    if(poly)
    {
        poly->show();
        poly->setSelected(true);
    }
    poly = dynamic_cast<MagicPolygon*>(obj);
    showObjectName(true,poly);
}

void EditorScene::showObjectName(bool show, MagicPolygon* poly)
{
    if (m_pObjectLabel)
    {
        if (show && poly)
        {
            m_pObjectLabel->setText(poly->data(POLYGON_NAME).toString());
        }
        else
        {
            m_pObjectLabel->setText("");
        }
    }
}

QColor EditorScene::getColorRGBA(const QString &name)
{
    QColor color;
    if (QColor::isValidColor(name))
        color.setNamedColor(name);
    else
    {
        // trying to parse "#RRGGBBAA" color
        if (name.length() == 9)
        {
            QString solidColor = name.left(7);
            if (QColor::isValidColor(solidColor))
            {
                color.setNamedColor(solidColor);
                int alpha = name.right(2).toInt(0, 16);
                color.setAlpha(alpha);
            }
        }
    }

    if (!color.isValid())
        qWarning() << QString("[ImageManager::resolveColor] Can\'t parse color: %1").arg(name);

    return color;
}

QString EditorScene::getColorStringRGBA(const QColor &color)
{
    QString hexColor(color.name());
    hexColor += QString::number(color.alpha(),16).left(2);
    return hexColor;
}

void EditorScene::hideOthers(QGraphicsItem* item)
{
    PolygonTool::hideAll();
    MagicPolygon* selected = magicpoly.value(item);
     foreach(MagicPolygon* pol,magicpoly)
    {
         if(pol!=selected){
            pol->hide();
        }
    }
}
QList<QGraphicsItem*> EditorScene::excludeTools(QList<QGraphicsItem*> selection)
 {
     QList<QGraphicsItem*> filtred;
     foreach(QGraphicsItem* item,selection)
     {

        if(!PolygonTool::isTool(item)&&!isPoly(item)&&isScenePoly(item))
        {
            filtred.push_back(item);
        }
     }
     return filtred;
}

QList<QGraphicsItem *> EditorScene::includeTools(QList<QGraphicsItem *> selection)
{
    QList<QGraphicsItem*> filtred;
    foreach(QGraphicsItem* item,selection)
    {
       if(!PolygonTool::isTool(item)&&!isPoly(item)&&isScenePoly(item))
       {
           filtred.push_back(item);
       }
    }
    return filtred;
}
bool EditorScene::isPoly(QGraphicsItem* item)
{
   if(magicpolywerif.constEnd()!=magicpolywerif.find(item))
   {
       return true;
   }
   return false;
}

QGraphicsItem* Tool::createItem(ItemType type)
{
    QGraphicsItem* item = NULL;
        switch(type)
        {
            case PointerItem:
            break;
            case PolygonItem:
            break;
            case RectItem: item = new QGraphicsRectItem(0,0,100,100);
            break;
            case EllipseItem: item = new QGraphicsEllipseItem(0,0,100,100);
            break;

            case SectorItem:
            {
                QPainterPath piePath;
                piePath.moveTo(50.0, 50.0);
                piePath.arcTo(0.0, 0.0, 100.0, 100.0, 90.0, 90.0);
                piePath.closeSubpath();
                item = new QGraphicsPathItem(piePath);
            }
            break;
            default:
            return NULL;
        }
        if(item)
        {
            item->setData(0,QVariant(int(type)));

        }
        return item;
}

EditorScene::Mode Tool::getMode(ItemType type)
{
    switch(type)
        {
            case PointerItem:
            return EditorScene::MoveItem;
            case PolygonItem:
            return EditorScene::InsertPolygon;
            case RectItem:
            return EditorScene::InsertItem;
            case EllipseItem:
            return EditorScene::InsertItem;
            case SectorItem:
            return EditorScene::InsertItem;
            case HandItem:
            return EditorScene::HandItem;
            default:
            return EditorScene::MoveItem;
        }

}
